/*
* Copyright (C) 1997-2001 Id Software, Inc.
* 
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
* 
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE.
* 
* See the GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307, USA.
*  
*/

// Created on 29.11.2003 by RST.
// $Id: MSG.java,v 1.8 2005/12/18 22:10:02 cawe Exp $
using System;
using Globals = Suake2.UI.Globals;
using Suake2.UI.game;
using Suake2.UI.util;
namespace Suake2.UI.qcommon
{
	
	public class MSG:Globals
	{
		
		//
		// writing functions
		//
		
		//ok.
		public static void  WriteChar(sizebuf_t sb, int c)
		{
			sb.data[SZ.GetSpace(sb, 1)] = (sbyte) (c & 0xFF);
		}
		
		//ok.
		public static void  WriteChar(sizebuf_t sb, float c)
		{
			
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			WriteChar(sb, (int) c);
		}
		
		//ok.
		public static void  WriteByte(sizebuf_t sb, int c)
		{
			sb.data[SZ.GetSpace(sb, 1)] = (sbyte) (c & 0xFF);
		}
		
		//ok.
		public static void  WriteByte(sizebuf_t sb, float c)
		{
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			WriteByte(sb, (int) c);
		}
		
		public static void  WriteShort(sizebuf_t sb, int c)
		{
			int i = SZ.GetSpace(sb, 2);
			sb.data[i++] = (sbyte) (c & 0xff);
			sb.data[i] = (sbyte) ((SupportClass.URShift(c, 8)) & 0xFF);
		}
		
		//ok.
		public static void  WriteInt(sizebuf_t sb, int c)
		{
			int i = SZ.GetSpace(sb, 4);
			sb.data[i++] = (sbyte) ((c & 0xff));
			sb.data[i++] = (sbyte) ((SupportClass.URShift(c, 8)) & 0xff);
			sb.data[i++] = (sbyte) ((SupportClass.URShift(c, 16)) & 0xff);
			sb.data[i++] = (sbyte) ((SupportClass.URShift(c, 24)) & 0xff);
		}
		
		//ok.
		public static void  WriteLong(sizebuf_t sb, int c)
		{
			WriteInt(sb, c);
		}
		
		//ok.
		public static void  WriteFloat(sizebuf_t sb, float f)
		{
			//UPGRADE_ISSUE: Method 'java.lang.Float.floatToIntBits' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javalangFloatfloatToIntBits_float'"
			WriteInt(sb, Float.floatToIntBits(f));
		}
		
		// had a bug, now its ok.
		public static void  WriteString(sizebuf_t sb, System.String s)
		{
			System.String x = s;
			
			if (s == null)
				x = "";
			
			SZ.Write(sb, Lib.stringToBytes(x));
			WriteByte(sb, 0);
			//Com.dprintln("MSG.WriteString:" + s.replace('\0', '@'));
		}
		
		//ok.
		public static void  WriteString(sizebuf_t sb, sbyte[] s)
		{
			WriteString(sb, new System.String(SupportClass.ToCharArray(SupportClass.ToByteArray(s))).Trim());
		}
		
		public static void  WriteCoord(sizebuf_t sb, float f)
		{
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			WriteShort(sb, (int) (f * 8));
		}
		
		public static void  WritePos(sizebuf_t sb, float[] pos)
		{
			//UPGRADE_ISSUE: The following fragment of code could not be parsed and was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1156'"
			assert(pos.length == 3): vec3_t bug;
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			WriteShort(sb, (int) (pos[0] * 8));
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			WriteShort(sb, (int) (pos[1] * 8));
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			WriteShort(sb, (int) (pos[2] * 8));
		}
		
		public static void  WriteAngle(sizebuf_t sb, float f)
		{
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			WriteByte(sb, (int) (f * 256 / 360) & 255);
		}
		
		public static void  WriteAngle16(sizebuf_t sb, float f)
		{
			WriteShort(sb, Math3D.ANGLE2SHORT(f));
		}
		
		public static void  WriteDeltaUsercmd(sizebuf_t buf, usercmd_t from, usercmd_t cmd)
		{
			int bits;
			
			//
			// send the movement message
			//
			bits = 0;
			if (cmd.angles[0] != from.angles[0])
				bits |= CM_ANGLE1;
			if (cmd.angles[1] != from.angles[1])
				bits |= CM_ANGLE2;
			if (cmd.angles[2] != from.angles[2])
				bits |= CM_ANGLE3;
			if (cmd.forwardmove != from.forwardmove)
				bits |= CM_FORWARD;
			if (cmd.sidemove != from.sidemove)
				bits |= CM_SIDE;
			if (cmd.upmove != from.upmove)
				bits |= CM_UP;
			if (cmd.buttons != from.buttons)
				bits |= CM_BUTTONS;
			if (cmd.impulse != from.impulse)
				bits |= CM_IMPULSE;
			
			WriteByte(buf, bits);
			
			if ((bits & CM_ANGLE1) != 0)
				WriteShort(buf, cmd.angles[0]);
			if ((bits & CM_ANGLE2) != 0)
				WriteShort(buf, cmd.angles[1]);
			if ((bits & CM_ANGLE3) != 0)
				WriteShort(buf, cmd.angles[2]);
			
			if ((bits & CM_FORWARD) != 0)
				WriteShort(buf, cmd.forwardmove);
			if ((bits & CM_SIDE) != 0)
				WriteShort(buf, cmd.sidemove);
			if ((bits & CM_UP) != 0)
				WriteShort(buf, cmd.upmove);
			
			if ((bits & CM_BUTTONS) != 0)
				WriteByte(buf, cmd.buttons);
			if ((bits & CM_IMPULSE) != 0)
				WriteByte(buf, cmd.impulse);
			
			WriteByte(buf, cmd.msec);
			WriteByte(buf, cmd.lightlevel);
		}
		
		//should be ok.
		public static void  WriteDir(sizebuf_t sb, float[] dir)
		{
			int i, best;
			float d, bestd;
			
			if (dir == null)
			{
				WriteByte(sb, 0);
				return ;
			}
			
			bestd = 0;
			best = 0;
			for (i = 0; i < NUMVERTEXNORMALS; i++)
			{
				d = Math3D.DotProduct(dir, bytedirs[i]);
				if (d > bestd)
				{
					bestd = d;
					best = i;
				}
			}
			WriteByte(sb, best);
		}
		
		//should be ok.
		public static void  ReadDir(sizebuf_t sb, float[] dir)
		{
			int b;
			
			b = ReadByte(sb);
			if (b >= NUMVERTEXNORMALS)
				Com.Error(ERR_DROP, "MSF_ReadDir: out of range");
			Math3D.VectorCopy(bytedirs[b], dir);
		}
		
		/*
		* ================== WriteDeltaEntity
		* 
		* Writes part of a packetentities message. Can delta from either a baseline
		* or a previous packet_entity ==================
		*/
		public static void  WriteDeltaEntity(entity_state_t from, entity_state_t to, sizebuf_t msg, bool force, bool newentity)
		{
			int bits;
			
			if (0 == to.number)
				Com.Error(ERR_FATAL, "Unset entity number");
			if (to.number >= MAX_EDICTS)
				Com.Error(ERR_FATAL, "Entity number >= MAX_EDICTS");
			
			// send an update
			bits = 0;
			
			if (to.number >= 256)
				bits |= U_NUMBER16; // number8 is implicit otherwise
			
			if (to.origin[0] != from.origin[0])
				bits |= U_ORIGIN1;
			if (to.origin[1] != from.origin[1])
				bits |= U_ORIGIN2;
			if (to.origin[2] != from.origin[2])
				bits |= U_ORIGIN3;
			
			if (to.angles[0] != from.angles[0])
				bits |= U_ANGLE1;
			if (to.angles[1] != from.angles[1])
				bits |= U_ANGLE2;
			if (to.angles[2] != from.angles[2])
				bits |= U_ANGLE3;
			
			if (to.skinnum != from.skinnum)
			{
				if (to.skinnum < 256)
					bits |= U_SKIN8;
				else if (to.skinnum < 0x10000)
					bits |= U_SKIN16;
				else
					bits |= (U_SKIN8 | U_SKIN16);
			}
			
			if (to.frame != from.frame)
			{
				if (to.frame < 256)
					bits |= U_FRAME8;
				else
					bits |= U_FRAME16;
			}
			
			if (to.effects != from.effects)
			{
				if (to.effects < 256)
					bits |= U_EFFECTS8;
				else if (to.effects < 0x8000)
					bits |= U_EFFECTS16;
				else
					bits |= U_EFFECTS8 | U_EFFECTS16;
			}
			
			if (to.renderfx != from.renderfx)
			{
				if (to.renderfx < 256)
					bits |= U_RENDERFX8;
				else if (to.renderfx < 0x8000)
					bits |= U_RENDERFX16;
				else
					bits |= U_RENDERFX8 | U_RENDERFX16;
			}
			
			if (to.solid != from.solid)
				bits |= U_SOLID;
			
			// event is not delta compressed, just 0 compressed
			if (to.event_Renamed != 0)
				bits |= U_EVENT;
			
			if (to.modelindex != from.modelindex)
				bits |= U_MODEL;
			if (to.modelindex2 != from.modelindex2)
				bits |= U_MODEL2;
			if (to.modelindex3 != from.modelindex3)
				bits |= U_MODEL3;
			if (to.modelindex4 != from.modelindex4)
				bits |= U_MODEL4;
			
			if (to.sound != from.sound)
				bits |= U_SOUND;
			
			if (newentity || (to.renderfx & RF_BEAM) != 0)
				bits |= U_OLDORIGIN;
			
			//
			// write the message
			//
			if (bits == 0 && !force)
				return ; // nothing to send!
			
			//----------
			
			if ((bits & unchecked((int) 0xff000000)) != 0)
				bits |= U_MOREBITS3 | U_MOREBITS2 | U_MOREBITS1;
			else if ((bits & 0x00ff0000) != 0)
				bits |= U_MOREBITS2 | U_MOREBITS1;
			else if ((bits & 0x0000ff00) != 0)
				bits |= U_MOREBITS1;
			
			WriteByte(msg, bits & 255);
			
			if ((bits & unchecked((int) 0xff000000)) != 0)
			{
				WriteByte(msg, (SupportClass.URShift(bits, 8)) & 255);
				WriteByte(msg, (SupportClass.URShift(bits, 16)) & 255);
				WriteByte(msg, (SupportClass.URShift(bits, 24)) & 255);
			}
			else if ((bits & 0x00ff0000) != 0)
			{
				WriteByte(msg, (SupportClass.URShift(bits, 8)) & 255);
				WriteByte(msg, (SupportClass.URShift(bits, 16)) & 255);
			}
			else if ((bits & 0x0000ff00) != 0)
			{
				WriteByte(msg, (SupportClass.URShift(bits, 8)) & 255);
			}
			
			//----------
			
			if ((bits & U_NUMBER16) != 0)
				WriteShort(msg, to.number);
			else
				WriteByte(msg, to.number);
			
			if ((bits & U_MODEL) != 0)
				WriteByte(msg, to.modelindex);
			if ((bits & U_MODEL2) != 0)
				WriteByte(msg, to.modelindex2);
			if ((bits & U_MODEL3) != 0)
				WriteByte(msg, to.modelindex3);
			if ((bits & U_MODEL4) != 0)
				WriteByte(msg, to.modelindex4);
			
			if ((bits & U_FRAME8) != 0)
				WriteByte(msg, to.frame);
			if ((bits & U_FRAME16) != 0)
				WriteShort(msg, to.frame);
			
			if ((bits & U_SKIN8) != 0 && (bits & U_SKIN16) != 0)
			//used for laser
			// colors
				WriteInt(msg, to.skinnum);
			else if ((bits & U_SKIN8) != 0)
				WriteByte(msg, to.skinnum);
			else if ((bits & U_SKIN16) != 0)
				WriteShort(msg, to.skinnum);
			
			if ((bits & (U_EFFECTS8 | U_EFFECTS16)) == (U_EFFECTS8 | U_EFFECTS16))
				WriteInt(msg, to.effects);
			else if ((bits & U_EFFECTS8) != 0)
				WriteByte(msg, to.effects);
			else if ((bits & U_EFFECTS16) != 0)
				WriteShort(msg, to.effects);
			
			if ((bits & (U_RENDERFX8 | U_RENDERFX16)) == (U_RENDERFX8 | U_RENDERFX16))
				WriteInt(msg, to.renderfx);
			else if ((bits & U_RENDERFX8) != 0)
				WriteByte(msg, to.renderfx);
			else if ((bits & U_RENDERFX16) != 0)
				WriteShort(msg, to.renderfx);
			
			if ((bits & U_ORIGIN1) != 0)
				WriteCoord(msg, to.origin[0]);
			if ((bits & U_ORIGIN2) != 0)
				WriteCoord(msg, to.origin[1]);
			if ((bits & U_ORIGIN3) != 0)
				WriteCoord(msg, to.origin[2]);
			
			if ((bits & U_ANGLE1) != 0)
				WriteAngle(msg, to.angles[0]);
			if ((bits & U_ANGLE2) != 0)
				WriteAngle(msg, to.angles[1]);
			if ((bits & U_ANGLE3) != 0)
				WriteAngle(msg, to.angles[2]);
			
			if ((bits & U_OLDORIGIN) != 0)
			{
				WriteCoord(msg, to.old_origin[0]);
				WriteCoord(msg, to.old_origin[1]);
				WriteCoord(msg, to.old_origin[2]);
			}
			
			if ((bits & U_SOUND) != 0)
				WriteByte(msg, to.sound);
			if ((bits & U_EVENT) != 0)
				WriteByte(msg, to.event_Renamed);
			if ((bits & U_SOLID) != 0)
				WriteShort(msg, to.solid);
		}
		
		//============================================================
		
		//
		// reading functions
		//
		
		public static void  BeginReading(sizebuf_t msg)
		{
			msg.readcount = 0;
		}
		
		// returns -1 if no more characters are available, but also [-128 , 127]
		public static int ReadChar(sizebuf_t msg_read)
		{
			int c;
			
			if (msg_read.readcount + 1 > msg_read.cursize)
				c = - 1;
			else
				c = msg_read.data[msg_read.readcount];
			msg_read.readcount++;
			// kickangles bugfix (rst)
			return c;
		}
		
		public static int ReadByte(sizebuf_t msg_read)
		{
			int c;
			
			if (msg_read.readcount + 1 > msg_read.cursize)
				c = - 1;
			else
				c = msg_read.data[msg_read.readcount] & 0xff;
			
			msg_read.readcount++;
			
			return c;
		}
		
		public static short ReadShort(sizebuf_t msg_read)
		{
			int c;
			
			if (msg_read.readcount + 2 > msg_read.cursize)
				c = - 1;
			else
				c = (short) ((msg_read.data[msg_read.readcount] & 0xff) + (msg_read.data[msg_read.readcount + 1] << 8));
			
			msg_read.readcount += 2;
			
			return (short) c;
		}
		
		public static int ReadLong(sizebuf_t msg_read)
		{
			int c;
			
			if (msg_read.readcount + 4 > msg_read.cursize)
			{
				Com.Printf("buffer underrun in ReadLong!");
				c = - 1;
			}
			else
				c = (msg_read.data[msg_read.readcount] & 0xff) | ((msg_read.data[msg_read.readcount + 1] & 0xff) << 8) | ((msg_read.data[msg_read.readcount + 2] & 0xff) << 16) | ((msg_read.data[msg_read.readcount + 3] & 0xff) << 24);
			
			msg_read.readcount += 4;
			
			return c;
		}
		
		public static float ReadFloat(sizebuf_t msg_read)
		{
			int n = ReadLong(msg_read);
			//UPGRADE_ISSUE: Method 'java.lang.Float.intBitsToFloat' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javalangFloatintBitsToFloat_int'"
			return Float.intBitsToFloat(n);
		}
		
		// 2k read buffer.
		public static sbyte[] readbuf = new sbyte[2048];
		
		public static System.String ReadString(sizebuf_t msg_read)
		{
			
			sbyte c;
			int l = 0;
			do 
			{
				c = (sbyte) ReadByte(msg_read);
				if (c == - 1 || c == 0)
					break;
				
				readbuf[l] = c;
				l++;
			}
			while (l < 2047);
			
			System.String ret = new System.String(SupportClass.ToCharArray(readbuf), 0, l);
			// Com.dprintln("MSG.ReadString:[" + ret + "]");
			return ret;
		}
		
		public static System.String ReadStringLine(sizebuf_t msg_read)
		{
			
			int l;
			sbyte c;
			
			l = 0;
			do 
			{
				c = (sbyte) ReadChar(msg_read);
				if (c == - 1 || c == 0 || c == 0x0a)
					break;
				readbuf[l] = c;
				l++;
			}
			while (l < 2047);
			
			System.String ret = new System.String(SupportClass.ToCharArray(readbuf), 0, l).Trim();
			Com.dprintln("MSG.ReadStringLine:[" + ret.Replace('\x0000', '@') + "]");
			return ret;
		}
		
		public static float ReadCoord(sizebuf_t msg_read)
		{
			return ReadShort(msg_read) * (1.0f / 8);
		}
		
		public static void  ReadPos(sizebuf_t msg_read, float[] pos)
		{
			//UPGRADE_ISSUE: The following fragment of code could not be parsed and was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1156'"
			assert(pos.length == 3): vec3_t bug;
			pos[0] = ReadShort(msg_read) * (1.0f / 8);
			pos[1] = ReadShort(msg_read) * (1.0f / 8);
			pos[2] = ReadShort(msg_read) * (1.0f / 8);
		}
		
		public static float ReadAngle(sizebuf_t msg_read)
		{
			return ReadChar(msg_read) * (360.0f / 256);
		}
		
		public static float ReadAngle16(sizebuf_t msg_read)
		{
			return Math3D.SHORT2ANGLE(ReadShort(msg_read));
		}
		
		public static void  ReadDeltaUsercmd(sizebuf_t msg_read, usercmd_t from, usercmd_t move)
		{
			int bits;
			
			//memcpy(move, from, sizeof(* move));
			// IMPORTANT!! copy without new
			move.set_Renamed(from);
			bits = ReadByte(msg_read);
			
			// read current angles
			if ((bits & CM_ANGLE1) != 0)
				move.angles[0] = ReadShort(msg_read);
			if ((bits & CM_ANGLE2) != 0)
				move.angles[1] = ReadShort(msg_read);
			if ((bits & CM_ANGLE3) != 0)
				move.angles[2] = ReadShort(msg_read);
			
			// read movement
			if ((bits & CM_FORWARD) != 0)
				move.forwardmove = ReadShort(msg_read);
			if ((bits & CM_SIDE) != 0)
				move.sidemove = ReadShort(msg_read);
			if ((bits & CM_UP) != 0)
				move.upmove = ReadShort(msg_read);
			
			// read buttons
			if ((bits & CM_BUTTONS) != 0)
				move.buttons = (sbyte) ReadByte(msg_read);
			
			if ((bits & CM_IMPULSE) != 0)
				move.impulse = (sbyte) ReadByte(msg_read);
			
			// read time to run command
			move.msec = (sbyte) ReadByte(msg_read);
			
			// read the light level
			move.lightlevel = (sbyte) ReadByte(msg_read);
		}
		
		public static void  ReadData(sizebuf_t msg_read, sbyte[] data, int len)
		{
			for (int i = 0; i < len; i++)
				data[i] = (sbyte) ReadByte(msg_read);
		}
	}
}